package client

import (
	"context"
	. "fastdp-ops/cmd/agent/config"
	. "fastdp-ops/cmd/agent/zaplog"
	pbMachine "fastdp-ops/proto/machine"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/connectivity"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/keepalive"
	"net"
	"sync"
	"time"
)

var (
	// 全局唯一连接（单例）
	globalConn *grpc.ClientConn
	// 保证并发安全的互斥锁
	connMux sync.Mutex
	// server 地址（从配置中读取，全局唯一）
	serverAddr string
)

// GetClientConn 获取agent连接（自动初始化/重建）
func GetClientConn() (*grpc.ClientConn, error) {
	// 从配置中获取服务端端口和IP（Viper配置键：fastdp-agent.rpcserver_port/host）
	serverPort := V.GetString("fastdp-agent.rpcserver_port")
	serverIP := V.GetString("fastdp-agent.rpcserver_host")
	serverAddr = net.JoinHostPort(serverIP, serverPort)
	// 打印待连接的服务端地址
	Logger.Info("初始化 server 地址", zap.String("address", serverAddr))

	// 快速检查：如果连接已存在且可用，直接返回（读锁优化）
	connMux.Lock()
	defer connMux.Unlock()
	// 检查当前连接状态
	if globalConn != nil {
		state := globalConn.GetState()
		// 连接处于 Ready（就绪）或 Idle（空闲）状态，直接复用
		if state == connectivity.Ready || state == connectivity.Idle {
			return globalConn, nil
		}
		// 连接已关闭（终态），主动释放旧连接
		if state == connectivity.Shutdown {
			Logger.Warn("旧连接已关闭，准备重建连接")
			if err := globalConn.Close(); err != nil {
				Logger.Warn("关闭旧连接失败", zap.Error(err))
			}
			globalConn = nil // 置空，触发重建
		} else {
			// 连接处于 TransientFailure（临时失败）或 Connecting（连接中），等待状态变化
			// 最多等待 3 秒，若仍未就绪则重建
			ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
			defer cancel()
			if !globalConn.WaitForStateChange(ctx, state) {
				Logger.Warn("连接状态无变化，准备重建连接", zap.String("state", state.String()))
				globalConn = nil // 置空，触发重建
			} else if globalConn.GetState() == connectivity.Ready {
				return globalConn, nil // 等待期间连接恢复，直接返回
			}
		}
	}
	// 连接不存在或已失效，创建新连接
	Logger.Info("开始创建新连接", zap.String("address", serverAddr))

	// 配置 gRPC 客户端参数（心跳、超时等）
	kp := keepalive.ClientParameters{
		Time:                15 * time.Second, // 每 15 秒发送一次心跳（保活）
		Timeout:             5 * time.Second,  // 心跳超时时间
		PermitWithoutStream: true,             // 无活跃流时也发送心跳（关键，避免连接被断开）
	}

	// 创建连接（带拨号超时）
	newConn, err := grpc.NewClient(
		serverAddr,
		grpc.WithTransportCredentials(insecure.NewCredentials()), // 无 TLS 加密（生产环境建议启用 TLS）
		grpc.WithKeepaliveParams(kp),
		grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
			// 控制 TCP 拨号超时（避免长时间阻塞）
			return net.DialTimeout("tcp", addr, 5*time.Second)
		}),
	)
	if err != nil {
		Logger.Error("创建新连接失败", zap.Error(err))
		return nil, err
	}
	// 验证连接是否就绪（避免创建后立即不可用）
	//state := newConn.GetState()
	//if state != connectivity.Ready {
	//	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	//	defer cancel()
	//	if !newConn.WaitForStateChange(ctx, state) || newConn.GetState() != connectivity.Ready {
	//		_ = newConn.Close() // 超时未就绪，关闭临时连接
	//		return nil, fmt.Errorf("连接创建后未就绪，状态: %s", newConn.GetState())
	//	}
	//}

	// 更新全局连接
	globalConn = newConn
	Logger.Info("新连接创建成功", zap.String("state", globalConn.GetState().String()))
	return globalConn, nil

}

// GetMachineClient 获取 MachineEnvService 客户端（基于全局连接）
func GetMachineClient() (pbMachine.MachineEnvServiceClient, error) {
	conn, err := GetClientConn()
	if err != nil {
		return nil, err
	}
	return pbMachine.NewMachineEnvServiceClient(conn), nil
}

// CloseConn 程序退出时关闭全局连接（需在 main 函数退出前调用）
func CloseConn() error {
	connMux.Lock()
	defer connMux.Unlock()
	if globalConn != nil {
		Logger.Info("开始关闭全局 gRPC 连接")
		err := globalConn.Close()
		globalConn = nil
		return err
	}
	return nil
}
